home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 5 / MacMania 5.toast / / Internet software / NewsWatcher / NW Source / Source / subscribe.c < prev    next >
Text File  |  1997-01-09  |  20KB  |  751 lines

  1. /*----------------------------------------------------------------------------
  2.  
  3.     subscribe.c
  4.  
  5.     This module handles subscribing and unsubscribing to groups.
  6.     
  7.     Copyright © 1994-1997, Northwestern University.
  8.  
  9. ----------------------------------------------------------------------------*/
  10.  
  11. #include <string.h>
  12. #include <stdio.h>
  13.  
  14. #include "glob.h"
  15. #include "dialog.h"
  16. #include "child.h"
  17. #include "newswatcher.h"
  18. #include "subscribe.h"
  19. #include "news.h"
  20. #include "memutil.h"
  21. #include "windutil.h"
  22. #include "strutil.h"
  23. #include "group.h"
  24. #include "full.h"
  25. #include "biglist.h"
  26.  
  27.  
  28.  
  29. #define kDuplicateGroupsAlert            151
  30.  
  31.  
  32.  
  33. /*----------------------------------------------------------------------------
  34.     PresentDuplicateGroupsAlert 
  35.     
  36.     Present the duplicate groups alert.
  37.     
  38.     Entry:    numDuplicates = number of duplicate groups.
  39.             dupNameOffset = offset in gGroupNames of duplicate group
  40.                 name if numDuplicates = 1.
  41.             drag = true if drag and drop, false if paste.
  42.                 
  43.     Exit:    function result = error code.
  44. ----------------------------------------------------------------------------*/
  45.  
  46. static OSErr PresentDuplicateGroupsAlert (long numDuplicates, 
  47.     long dupNameOffset, Boolean drag)
  48. {
  49.     CStr255 msg, fmt, groupName, movingOrPasting;
  50.     DialogPtr dlg;
  51.     short len, groupNameLen, item;
  52.     OSErr err = noErr;
  53.     
  54.     GetCString(drag ? kStrMoving : kStrPasting, movingOrPasting);
  55.     
  56.     if (numDuplicates > 1) {
  57.         GetCString(kStrDuplicateGroups, fmt);
  58.         sprintf(msg, fmt, movingOrPasting, movingOrPasting);
  59.     } else {
  60.         GetCString(kStrOneDuplicateGroup, fmt);
  61.         strcpy(groupName, *gGroupNames + dupNameOffset);
  62.         groupNameLen = strlen(groupName);
  63.         len = strlen(fmt) + groupNameLen + strlen(movingOrPasting) - 4;
  64.         if (len > 255) {
  65.             groupNameLen -= len - 255;
  66.             groupName[groupNameLen-3] = '.'; 
  67.             groupName[groupNameLen-2] = '.'; 
  68.             groupName[groupNameLen-1] = '.'; 
  69.             groupName[groupNameLen] = 0;
  70.         }
  71.         sprintf(msg, fmt, groupName, movingOrPasting);
  72.     }
  73.     c2pstr(msg);
  74.  
  75.     err = MyGetNewDialog(kDuplicateGroupsAlert, ok, cancel, &dlg);
  76.     if (err != noErr) return err;
  77.     ParamText((StringPtr)msg, "\p", "\p", "\p");
  78.     SysBeep(0);
  79.     MyModalDialog(dlg, gDialogFilterUPP, &item);
  80.     err = DoClose(dlg);
  81.     if (err != noErr) return err;
  82.     if (item == cancel) return userCanceledErr;
  83.     return noErr;
  84. }
  85.  
  86.  
  87.  
  88. /*----------------------------------------------------------------------------
  89.     ClearStatus 
  90.     
  91.     Clear the status fields in all groups in a user group window.
  92.     
  93.     Entry:    wind = pointer to user group list window.
  94. ----------------------------------------------------------------------------*/
  95.  
  96. static void ClearStatus (WindowPtr wind)
  97. {
  98.     TWindow **info;
  99.     BigListRef groupList;
  100.     TGroup **groupArray;
  101.     long item, index, numItems;
  102.     
  103.     info = (TWindow**)GetWRefCon(wind);
  104.     groupList = (**info).groupList;
  105.     groupArray = (**info).groupArray;
  106.     numItems = BigLGetNumItems(groupList);
  107.     for (item = 0; item < numItems; item++) {
  108.         index = BigLGetData(groupList, item);
  109.         (*groupArray)[index].status = ' ';
  110.     }
  111. }
  112.  
  113.  
  114.  
  115. /*----------------------------------------------------------------------------
  116.     GroupInList 
  117.     
  118.     Check to see if a named group appears in a user group list window, and
  119.     set the status field to 'x' if the group does appear.
  120.     
  121.     Entry:    nameOffset = offset in gGroupNames of group name.
  122.             wind = pointer to user group list window.
  123.                 
  124.     Exit:    function result = true if group in window.
  125. ----------------------------------------------------------------------------*/
  126.  
  127. static Boolean GroupInList (long nameOffset, WindowPtr wind)
  128. {
  129.     TWindow **info;
  130.     BigListRef groupList;
  131.     TGroup **groupArray;
  132.     long numItems, index, item;
  133.     
  134.     info = (TWindow**)GetWRefCon(wind);
  135.     groupList = (**info).groupList;
  136.     groupArray = (**info).groupArray;
  137.     numItems = BigLGetNumItems(groupList);
  138.     for (item = 0; item < numItems; item++) {
  139.         index = BigLGetData(groupList, item);
  140.         if ((*groupArray)[index].nameOffset == nameOffset) {
  141.             (*groupArray)[index].status = 'x';
  142.             return true;
  143.         }
  144.     }
  145.     return false;
  146. }
  147.  
  148.  
  149.  
  150. /*----------------------------------------------------------------------------
  151.     RemoveDuplicates 
  152.     
  153.     Remove duplicate groups (the ones marked with status 'x').
  154.     
  155.     Entry:    wind = pointer to user group list window.
  156.     
  157.     Exit:    function result = error code.
  158. ----------------------------------------------------------------------------*/
  159.  
  160. static OSErr RemoveDuplicates (WindowPtr wind)
  161. {
  162.     WindowPtr child;
  163.     TWindow **info;
  164.     BigListRef groupList;
  165.     TGroup **groupArray, groupInfo;
  166.     long numDel = 0;
  167.     OSErr err = noErr;
  168.     long index, numItems, item;
  169.     
  170.     info = (TWindow**)GetWRefCon(wind);
  171.     groupList = (**info).groupList;
  172.     groupArray = (**info).groupArray;
  173.     numItems = BigLGetNumItems(groupList);
  174.     
  175.     for (item = 0; item < numItems; item++) {
  176.         index = BigLGetData(groupList, item);
  177.         groupInfo = (*groupArray)[index];
  178.         if (groupInfo.status == 'x') {
  179.             if ((child = FindChild(wind, item)) != nil) {
  180.                 err = DoClose(child);
  181.                 if (err != noErr) return err;
  182.             }
  183.             DisposeGroupUnreadList(&groupInfo);
  184.             (*groupArray)[index] = groupInfo;
  185.             BigLDeleteItems(groupList, item, 1);
  186.             numDel++;
  187.             item--;
  188.             numItems--;
  189.         }
  190.     }
  191.     return noErr;
  192. }
  193.  
  194.  
  195.  
  196.  
  197. /*----------------------------------------------------------------------------
  198.     RedrawGroupCount 
  199.     
  200.     Redraw the count in the panel area of a group window.
  201.     
  202.     Entry:    wind = pointer to group window.
  203. ----------------------------------------------------------------------------*/
  204.  
  205. static void RedrawGroupCount (WindowPtr wind)
  206. {
  207.     GrafPtr port;
  208.     TWindow **info;
  209.     Rect r;
  210.  
  211.     GetPort(&port);
  212.     SetPort(wind);
  213.     info = (TWindow**)GetWRefCon(wind);
  214.     r = wind->portRect;
  215.     r.bottom = (**info).panelHeight-3;
  216.     InvalRect(&r);
  217.     SetPort(port);
  218. }
  219.  
  220.  
  221.  
  222. /*----------------------------------------------------------------------------
  223.     RemoveGroupFromUnsubscribedList 
  224.     
  225.     Remove a group from the unsubscribed list.
  226.     
  227.     Entry:    groupName = group name.
  228.             unsubscribed = handle to unsubscribed list.
  229. ----------------------------------------------------------------------------*/
  230.  
  231. void RemoveGroupFromUnsubscribedList (char *groupName, Handle unsubscribed)
  232. {
  233.     long len, unsubscribedLen;
  234.     char *p, *pEnd, *q;
  235.  
  236.     if (unsubscribed == nil) return;
  237.     len = strlen(groupName);
  238.     unsubscribedLen = MyGetHandleSize(unsubscribed);
  239.     p = *unsubscribed;
  240.     pEnd = p + unsubscribedLen;
  241.     while (p < pEnd) {
  242.         q = p;
  243.         while (q < pEnd && *q != CR) q++;
  244.         q++;
  245.         if (MyStrNEqual(p, groupName, len)) break;
  246.         p = q;
  247.     }
  248.     if (p > pEnd) return;
  249.     len = q-p;
  250.     BlockMoveData(q, p, pEnd-q);
  251.     MySetHandleSize(unsubscribed, unsubscribedLen - len);
  252. }
  253.  
  254.  
  255.  
  256. /*----------------------------------------------------------------------------
  257.     AddNewGroup 
  258.     
  259.     Add a new group to a user group window.
  260.     
  261.     Entry:    nameOffset = offset in gGroupNames of group name to add.
  262.             wind = pointer to user group window.
  263.             pos = position of new user group list entry (item number
  264.                 of new item). Pass kMaxLong to add at end of list.
  265.             theGroup = pointer to group info to add, or nil to get info
  266.                 for new group from server.
  267.             cloneUnreadList = true to clone a copy of the unread list
  268.                 in theGroup.
  269.  
  270.     Exit:    function result = error code.
  271. ----------------------------------------------------------------------------*/
  272.  
  273. OSErr AddNewGroup (long nameOffset, WindowPtr wind, long pos, 
  274.     TGroup *theGroup, Boolean cloneUnreadList)
  275. {
  276.     BigListRef groupList;
  277.     TWindow **info;
  278.     TGroup **groupArray, newGroup;
  279.     long newGroupIndex, numItems;
  280.     OSErr err = noErr;
  281.     Boolean groupExists;
  282.     TUnread **unread, **newUnread, **prevNewUnread;
  283.     CStr255 groupName;
  284.     
  285.     newGroup.firstMess = 1;
  286.     newGroup.lastMess = 0;
  287.     newGroup.numUnread = 0;
  288.     newGroup.unread = nil;
  289.     newGroup.status = ' ';
  290.  
  291.     info = (TWindow**)GetWRefCon(wind);
  292.     
  293.     groupList = (**info).groupList;
  294.     groupArray = (**info).groupArray;
  295.     strcpy(groupName, *gGroupNames + nameOffset);
  296.     
  297.     if (theGroup == nil) {
  298.         newGroup.nameOffset = nameOffset;
  299.         err = GetGroupArticleRange(&newGroup, &groupExists);
  300.         if (err != noErr) goto exit;
  301.         if (groupExists && newGroup.firstMess <= newGroup.lastMess) {
  302.             err = MyNewHandle(sizeof(TUnread), &unread);
  303.             if (err != noErr) goto exit;
  304.             (**unread).firstUnread = newGroup.firstMess;
  305.             (**unread).lastUnread = newGroup.lastMess;
  306.             (**unread).next = nil;
  307.             newGroup.unread = unread;
  308.         }
  309.     } else {
  310.         newGroup = *theGroup;
  311.         if (cloneUnreadList) {
  312.             newGroup.unread = nil;
  313.             unread = theGroup->unread;
  314.             prevNewUnread = nil;
  315.             while (unread != nil) {
  316.                 err = MyNewHandle(sizeof(TUnread), &newUnread);
  317.                 if (err != noErr) goto exit;
  318.                 **newUnread = **unread;
  319.                 (**newUnread).next = nil;
  320.                 if (prevNewUnread == nil) {
  321.                     newGroup.unread = newUnread;
  322.                 } else {
  323.                     (**prevNewUnread).next = newUnread;
  324.                 }
  325.                 prevNewUnread = newUnread;
  326.                 unread = (**unread).next;
  327.             }
  328.         }
  329.     }
  330.  
  331.     err = MySetHandleSize(groupArray, MyGetHandleSize(groupArray) + sizeof(TGroup));
  332.     if (err != noErr) goto exit;
  333.     newGroupIndex = (**info).numGroups;
  334.     (**info).numGroups++;
  335.     (*groupArray)[newGroupIndex] = newGroup;
  336.     
  337.     numItems = BigLGetNumItems(groupList);
  338.     if (pos > numItems) pos = numItems;
  339.  
  340.     BigLAddItems(groupList, pos, 1);
  341.     BigLSetData(groupList, pos, newGroupIndex);
  342.     
  343.     RemoveGroupFromUnsubscribedList(groupName, (**info).unsubscribed);
  344.     
  345.     (**info).changed = true;
  346.     return noErr;
  347.     
  348. exit:
  349.  
  350.     DisposeGroupUnreadList(&newGroup);
  351.     return err;
  352. }
  353.  
  354.  
  355.  
  356. /*----------------------------------------------------------------------------
  357.     CopyGroupsFromScrap 
  358.     
  359.     Copy groups from scrap data to a user group list window.
  360.     
  361.     Entry:    data = handle to scrap data. WARNING: The scrap data is
  362.                 modified by this function.
  363.             wind = pointer to destination user group list window.
  364.             pos = position of new user group list items in destination
  365.                 window (starting item number of new items). 
  366.                 Pass kMaxLong to add at end of list.
  367.                 
  368.     Exit:    function result = error code.
  369. ----------------------------------------------------------------------------*/
  370.  
  371. OSErr CopyGroupsFromScrap (Handle data, WindowPtr wind, long pos)
  372. {
  373.     TWindow **info;
  374.     BigListRef groupList;
  375.     long dataPos, groupNameLen, nameOffset, numUnreadPairs;
  376.     long numGroups, i, dupNameOffset;
  377.     long firstPos;
  378.     CStr255 groupName;
  379.     TGroup theGroup;
  380.     TUnread **prevUnread, **unread;
  381.     OSErr err = noErr;
  382.     long numSubscribed, item, numItems;
  383.     long numDuplicates, numNotInFullGroupList;
  384.     long j, jDataPos, jGroupNameLen;
  385.     
  386.     theGroup.unread = nil;
  387.     
  388.     err = CheckScrapData(data);
  389.     if (err != noErr) goto exit;
  390.     
  391.     info = (TWindow**)GetWRefCon(wind);
  392.     groupList = (**info).groupList;
  393.     numItems = BigLGetNumItems(groupList);
  394.     if (pos > numItems) pos = numItems;
  395.     
  396.     dataPos = 12;
  397.     numGroups = *(long*)(*data + dataPos);
  398.     dataPos += sizeof(long);
  399.     
  400.     ClearStatus(wind);
  401.     
  402.     /* Check for duplicate groups, both duplicates within the scrap data
  403.        and duplicates between the scrap data and the target window. Also 
  404.        check for groups which no longer exist in the full group list.
  405.        
  406.        For each group in the scrap data, the byte immediately following 
  407.        the group name length byte is set to 0 to skip the group, either 
  408.        because it is a duplicate within the scrap data or because it no 
  409.        longer exists in the full group list. */
  410.     
  411.     numDuplicates = 0;
  412.     numNotInFullGroupList = 0;
  413.     for (i = 0; i < numGroups; i++) {
  414.         groupNameLen = *(unsigned char*)(*data + dataPos);
  415.         dataPos++;
  416.         BlockMoveData(*data + dataPos, groupName, groupNameLen);
  417.         *(groupName + groupNameLen) = 0;
  418.         nameOffset = FindGroupOffset(groupName);
  419.         if (nameOffset == -1) {
  420.             numNotInFullGroupList++;
  421.         } else {
  422.             for (j = 0, jDataPos = 16; j < i; j++) {
  423.                 jGroupNameLen = *(unsigned char*)(*data + jDataPos);
  424.                 jDataPos++;
  425.                 if (jGroupNameLen == groupNameLen &&
  426.                     strncmp(*data + jDataPos, groupName, groupNameLen) == 0)
  427.                 {
  428.                     nameOffset = -1;
  429.                     break;
  430.                 }
  431.                 jDataPos += jGroupNameLen;
  432.                 jDataPos = ((jDataPos + 3) >> 2) << 2;
  433.                 jDataPos += 3*sizeof(long);
  434.                 numUnreadPairs = *(long*)(*data + jDataPos);
  435.                 jDataPos += sizeof(long);
  436.                 jDataPos += numUnreadPairs * 2 * sizeof(long);
  437.             }
  438.             if (nameOffset >= 0 && GroupInList(nameOffset, wind)) {
  439.                 dupNameOffset = nameOffset;
  440.                 numDuplicates++;
  441.             }
  442.         }
  443.         if (nameOffset == -1) *(*data + dataPos) = 0;
  444.         dataPos += groupNameLen;
  445.         dataPos = ((dataPos + 3) >> 2) << 2;
  446.         dataPos += 3*sizeof(long);
  447.         numUnreadPairs = *(long*)(*data + dataPos);
  448.         dataPos += sizeof(long);
  449.         dataPos += numUnreadPairs * 2 * sizeof(long);
  450.     }
  451.     
  452.     if (numDuplicates > 0) {
  453.         err = PresentDuplicateGroupsAlert(numDuplicates, dupNameOffset, true);
  454.         if (err != noErr) goto exit;
  455.     }
  456.     
  457.     dataPos = 16;
  458.     firstPos = pos;
  459.     numSubscribed = 0;
  460.     for (i = 0; i < numGroups; i++) {
  461.         groupNameLen = *(unsigned char*)(*data + dataPos);
  462.         dataPos++;
  463.         BlockMoveData(*data + dataPos, groupName, groupNameLen);
  464.         dataPos += groupNameLen;
  465.         dataPos = ((dataPos + 3) >> 2) << 2;
  466.         if (*groupName == 0) {
  467.             dataPos += 3*sizeof(long);
  468.             numUnreadPairs = *(long*)(*data + dataPos);
  469.             dataPos += sizeof(long);
  470.             dataPos += numUnreadPairs * 2 * sizeof(long);
  471.         } else {
  472.             *(groupName + groupNameLen) = 0;
  473.             nameOffset = FindGroupOffset(groupName);
  474.             theGroup.nameOffset = nameOffset;
  475.             theGroup.firstMess = *(long*)(*data + dataPos);
  476.             dataPos += sizeof(long);
  477.             theGroup.lastMess = *(long*)(*data + dataPos);
  478.             dataPos += sizeof(long);
  479.             theGroup.numUnread = *(long*)(*data + dataPos);
  480.             dataPos += sizeof(long);
  481.             numUnreadPairs = *(long*)(*data + dataPos);
  482.             dataPos += sizeof(long);
  483.             prevUnread = nil;
  484.             while (numUnreadPairs--) {
  485.                 err = MyNewHandle(sizeof(TUnread), &unread);
  486.                 if (err != noErr) goto exit;
  487.                 (**unread).firstUnread = *(long*)(*data + dataPos);
  488.                 dataPos += sizeof(long);
  489.                 (**unread).lastUnread = *(long*)(*data + dataPos);
  490.                 dataPos += sizeof(long);
  491.                 (**unread).next = nil;
  492.                 if (prevUnread == nil) {
  493.                     theGroup.unread = unread;
  494.                 } else {
  495.                     (**prevUnread).next = unread;
  496.                 }
  497.                 prevUnread = unread;
  498.             }
  499.             if (theGroup.firstMess >= 0) {
  500.                 err = AddNewGroup(nameOffset, wind, pos, &theGroup, false);
  501.                 if (err != noErr) goto exit;
  502.                 theGroup.unread = nil;
  503.             } else {
  504.                 err = AddNewGroup(nameOffset, wind, pos, nil, false);
  505.                 if (err != noErr) goto exit;
  506.                 theGroup.unread = nil;
  507.             }
  508.             pos++;
  509.             numSubscribed++;
  510.         }
  511.     }
  512.     
  513.     if (numNotInFullGroupList) NoteMessageNumber(kStrSomeGroupsNotInFullGroupList);
  514.     
  515.     if (numSubscribed == 0) goto exit;
  516.     
  517.     BigLSelectAll(groupList, false);
  518.     for (item = firstPos; item < firstPos + numSubscribed; item++)
  519.         BigLSelect(groupList, item, true);
  520.         
  521.     if (numDuplicates > 0) {
  522.         err = RemoveDuplicates(wind);
  523.         if (err != noErr) goto exit;
  524.     }
  525.             
  526.     if (gPrefs.reZoomWindows) {
  527.         err = DoZoom(wind, inZoomOut);
  528.         if (err != noErr) goto exit;
  529.     } else {
  530.         SetWindowNeedsZooming(wind);
  531.     }
  532.     RedrawGroupCount(wind);
  533.     
  534.     return noErr;
  535.     
  536. exit:
  537.  
  538.     DisposeGroupUnreadList(&theGroup);
  539.     return err;
  540. }
  541.  
  542.  
  543.  
  544. /*----------------------------------------------------------------------------
  545.     CopyOrMoveSelectedGroups 
  546.     
  547.     Copy or move selected groups from a group list window to a user group
  548.     list window.
  549.     
  550.     Entry:    srcWindow = pointer to source group list window.
  551.             destWindow = pointer to destination user group list window.
  552.             pos = position of new user group list items in destination
  553.                 window (starting item number of new items). 
  554.                 Pass kMaxLong to add at end of list.
  555.             copy = true to copy groups, false to move groups.
  556.                 
  557.     Exit:    function result = error code.
  558. ----------------------------------------------------------------------------*/
  559.  
  560. OSErr CopyOrMoveSelectedGroups (WindowPtr srcWindow, WindowPtr destWindow, long pos,
  561.     Boolean copy)
  562. {
  563.     long srcItem, destItem;
  564.     TWindow **srcInfo, **destInfo;
  565.     TGroup **srcGroupArray, srcGroup;
  566.     long **srcGroupNameOffsets;
  567.     BigListRef srcList, destList;
  568.     long index;
  569.     long numSelected=0, firstPos, numGroups;
  570.     OSErr err = noErr;
  571.     TGroupWindowKind srcKind;
  572.     long numDuplicates;
  573.     long nameOffset, dupNameOffset;
  574.     
  575.     srcInfo = (TWindow**)GetWRefCon(srcWindow);
  576.     srcList = (**srcInfo).groupList;
  577.     srcGroupArray = (**srcInfo).groupArray;
  578.     srcGroupNameOffsets = (**srcInfo).groupNameOffsets;
  579.     srcKind = (**srcInfo).groupKind;
  580.     destInfo = (TWindow**)GetWRefCon(destWindow);
  581.     destList = (**destInfo).groupList;
  582.     numGroups = BigLGetNumItems(destList);
  583.     if (pos > numGroups) pos = numGroups;
  584.     
  585.     ClearStatus(destWindow);
  586.     
  587.     srcItem = BigLGetFirstSelectedItem(srcList);
  588.     numDuplicates = 0;
  589.     while (srcItem >= 0) {
  590.         index = BigLGetData(srcList, srcItem);
  591.         if (srcKind == kUserGroup) {
  592.             nameOffset = (*srcGroupArray)[index].nameOffset;
  593.         } else {
  594.             nameOffset = (*srcGroupNameOffsets)[index];
  595.         }
  596.         if (GroupInList(nameOffset, destWindow)) {
  597.             dupNameOffset = nameOffset;
  598.             numDuplicates++;
  599.         }
  600.         srcItem = BigLGetNextSelectedItem(srcList, srcItem+1);
  601.     }
  602.     
  603.     if (numDuplicates > 0) {
  604.         err = PresentDuplicateGroupsAlert(numDuplicates, dupNameOffset, true);
  605.         if (err != noErr) return err;
  606.     }
  607.     
  608.     srcItem = BigLGetFirstSelectedItem(srcList);
  609.     firstPos = pos;
  610.     while (srcItem >= 0) {
  611.         numSelected++;
  612.         index = BigLGetData(srcList, srcItem);
  613.         if (srcKind == kUserGroup) {
  614.             srcGroup = (*srcGroupArray)[index];
  615.             err = AddNewGroup(srcGroup.nameOffset, destWindow, pos,
  616.                 &srcGroup, copy);
  617.             if (err != noErr) return err;
  618.             if (!copy) {
  619.                 srcGroup.unread = nil;
  620.                 (*srcGroupArray)[index] = srcGroup;
  621.             }
  622.         } else {
  623.             err = AddNewGroup((*srcGroupNameOffsets)[index], destWindow, pos,
  624.                 nil, false);
  625.             if (err != noErr) return err;
  626.         }
  627.         pos++;
  628.         srcItem = BigLGetNextSelectedItem(srcList, srcItem+1);
  629.     }
  630.     
  631.     if (numSelected == 0) return noErr;
  632.     
  633.     if (!copy) {
  634.         err = UnsubscribeSelected(srcWindow);
  635.         if (err != noErr) return err;
  636.     }
  637.     
  638.     BigLSelectAll(destList, false);
  639.     for (destItem = firstPos; destItem < firstPos + numSelected; destItem++)
  640.         BigLSelect(destList, destItem, true);
  641.         
  642.     if (numDuplicates > 0) {
  643.         err = RemoveDuplicates(destWindow);
  644.         if (err != noErr) return err;
  645.     }
  646.         
  647.     if (gPrefs.reZoomWindows) {
  648.         err = DoZoom(destWindow, inZoomOut);
  649.         if (err != noErr) return err;
  650.     } else {
  651.         SetWindowNeedsZooming(destWindow);
  652.     }
  653.     RedrawGroupCount(destWindow);
  654.  
  655.     return noErr;
  656. }
  657.  
  658.  
  659.  
  660. /*----------------------------------------------------------------------------
  661.     AddGroupToUnsubscribedList 
  662.     
  663.     Add a group to the unsubscribed list.
  664.     
  665.     Entry:    groupName = group name.
  666.             unsubscribed = handle to unsubscribed list.
  667.             
  668.     Exit:    function result = error code.
  669. ----------------------------------------------------------------------------*/
  670.  
  671. OSErr AddGroupToUnsubscribedList (char *groupName, Handle unsubscribed)
  672. {
  673.     long len, unsubscribedLen;
  674.     char *p, *pEnd, *q;
  675.     OSErr err = noErr;
  676.  
  677.     if (unsubscribed == nil) return noErr;
  678.     len = strlen(groupName);
  679.     unsubscribedLen = MyGetHandleSize(unsubscribed);
  680.     p = *unsubscribed;
  681.     pEnd = p + unsubscribedLen;
  682.     while (p < pEnd) {
  683.         q = p;
  684.         while (q < pEnd && *q != CR) q++;
  685.         q++;
  686.         if (MyStrNEqual(p, groupName, len)) break;
  687.         p = q;
  688.     }
  689.     if (p < pEnd) return noErr;
  690.     err = MySetHandleSize(unsubscribed, unsubscribedLen + len + 2);
  691.     if (err != noErr) return err;
  692.     p = *unsubscribed + unsubscribedLen;
  693.     BlockMoveData(groupName, p, len);
  694.     p += len;
  695.     *p++ = '!';
  696.     *p++ = CR;
  697.     return noErr;
  698. }
  699.  
  700.  
  701.  
  702. /*----------------------------------------------------------------------------
  703.     UnsubscribeSelected 
  704.     
  705.     Unsubscribe selected newsgroups.
  706.     
  707.     Entry:    wind = pointer to user group list window.
  708.     
  709.     Exit:    function result = error code.
  710. ----------------------------------------------------------------------------*/
  711.  
  712. OSErr UnsubscribeSelected (WindowPtr wind)
  713. {
  714.     WindowPtr child;
  715.     TWindow **info;
  716.     BigListRef groupList;
  717.     TGroup **groupArray, groupInfo;
  718.     long item, index, numDel = 0;
  719.     OSErr err = noErr;
  720.     CStr255 groupName;
  721.     
  722.     info = (TWindow**)GetWRefCon(wind);
  723.     groupList = (**info).groupList;
  724.     groupArray = (**info).groupArray;
  725.     
  726.     item = BigLGetFirstSelectedItem(groupList);
  727.     while (item >= 0) {
  728.         index = BigLGetData(groupList, item);
  729.         if ((child = FindChild(wind, item)) != nil) {
  730.             err = DoClose(child);
  731.             if (err != noErr) return err;
  732.         }
  733.         groupInfo = (*groupArray)[index];
  734.         DisposeGroupUnreadList(&groupInfo);
  735.         (*groupArray)[index] = groupInfo;
  736.         strcpy(groupName, *gGroupNames + groupInfo.nameOffset);
  737.         err = AddGroupToUnsubscribedList(groupName, (**info).unsubscribed);
  738.         if (err != noErr) return err;
  739.         BigLDeleteItems(groupList, item, 1);
  740.         numDel++;
  741.         item = BigLGetNextSelectedItem(groupList, item);
  742.     }
  743.     if (numDel > 0) {
  744.         (**info).changed = true;
  745.         RedrawGroupCount(wind);
  746.         SetWindowNeedsZooming(wind);
  747.     }
  748.     return noErr;
  749. }
  750.  
  751.